package encaps.utils.html;

import encaps.utils.ImageServlet;
import static encaps.utils.html.HTML.*;
import java.util.*;

public class HTMLElement implements Cloneable {

    protected List children;
    protected Map<String, String> attributes;
    private String name;

    /**
     * Constructs a new HTMLElement which represents an HTML tag with given
     * name.
     *
     * @param name tag name.
     */
    public HTMLElement(String name) {
        this.name = name;
    }

    /**
     * Constructs a new HTMLElement which represents an HTML tag with given name
     * and inner content.
     *
     * @param name tag name.
     * @param content content inside this HTML element.
     */
    public HTMLElement(String name, String content) {
        this(name);
        addContent(content);
    }

    /**
     * Creates line-break element.
     *
     * @return Line-break HTMLElement.
     */
    public static HTMLElement createBr() {
        return new HTMLElement(BR);
    }

    /**
     * Creates creates HTML element which sets font to boldface.
     *
     * @param content inner content of the element.
     * @return
     * <code>b</code> HTML element with given inner content.
     */
    public static HTMLElement createB(String content) {
        HTMLElement b = new HTMLElement(B, content);
        return b;
    }

    /**
     * Creates HTML button with
     * <code>button</code> tag.
     *
     * @param name name of the button, a value of
     * <code>name</code> attribute.
     * @param label label on the button (content inside
     * <code>button</code> tag).
     * @param value value of the button, a value of
     * <code>value</code> attribute.
     * @return HTMLElement representing button.
     */
    public static HTMLElement createButton(String name, String value, String label) {
        HTMLElement button = new HTMLElement(BUTTON);
        button.addAttribute(NAME, name);
        button.addAttribute(VALUE, value);
        button.addContent(label);
        return button;
    }

    public static HTMLElement createImageButton(String name, String value, String img) {
        HTMLElement imagebutton = new HTMLElement(BUTTON);
        imagebutton.addAttribute(TYPE, IMAGE);
        imagebutton.addAttribute(NAME, name);
        imagebutton.addAttribute(VALUE, value);

        HTMLElement image = new HTMLElement(IMG);
        image.addAttribute(SRC, String.format("%s?%s=%s",
                ImageServlet.IMAGE_SERVLET_NAME, ImageServlet.PATH, img));
        image.addAttribute(ALT, value);
        imagebutton.addContent(image);

        return imagebutton;
    }

    public static HTMLElement createRadioButton(String name, String value, String label) {
        HTMLElement radio = new HTMLElement(INPUT);
        radio.addAttribute(TYPE, RADIO);
        radio.addAttribute(NAME, name);
        radio.addAttribute(VALUE, value);
        radio.addContent(label);
        return radio;
    }

    /**
     * Creates HTML text field.
     *
     * @param name name of the text field, a value of
     * <code>name</code> attribute.
     * @param size size of text field.
     * @param value initial value in text field.
     * @return HTMLElement representing text field.
     */
    public static HTMLElement createTextField(String name, int size, String value) {
        HTMLElement textField = createTextField(name, size);
        textField.addContent(value);
        return textField;
    }

    /**
     * Creates HTML text field.
     *
     * @param name name of the text field, a value of
     * <code>name</code> attribute.
     * @param size size of text field.
     * @return HTMLElement representing text field.
     */
    public static HTMLElement createTextField(String name, int size) {
        HTMLElement textField = new HTMLElement(INPUT);
        textField.addAttribute(NAME, name);
        textField.addAttribute(TYPE, TEXT);
        textField.addAttribute(SIZE, size);
        return textField;
    }

    /**
     * Puts text content inside this HTML element.
     *
     * @param content a text content to put.
     * @return this HTMLElement.
     */
    public final HTMLElement addContent(String content) {
        if (content == null) {
            return this;
        }
        if (children == null) {
            children = new LinkedList();
        }
        children.add(content);
        return this;
    }

    /**
     * Puts numeric content inside this HTML element.
     *
     * @param content a text content to put.
     * @return this HTMLElement.
     */
    public final HTMLElement addContent(Number content) {
        return addContent(content.toString());
    }

    /**
     * Puts child HTML element inside this HTML element.
     *
     * @param content a child element to put.
     * @return this HTMLElement.
     */
    public HTMLElement addContent(HTMLElement element) {
        if (element == null) {
            return this;
        }
        if (children == null) {
            children = new LinkedList();
        }
        children.add(element);
        return this;
    }

    /**
     * Adds an attribute with text value to given HTML element.
     *
     * @param name attribute name.
     * @param value attribute value.
     * @return this HTMLElement.
     */
    public final HTMLElement addAttribute(String name, String value) {
        if (attributes == null) {
            attributes = new HashMap<String, String>();
        }
        attributes.put(name, value);
        return this;
    }

    /**
     * Adds an attribute with numeric value to given HTML element.
     *
     * @param name attribute name.
     * @param value attribute value.
     * @return this HTMLElement.
     */
    public final HTMLElement addAttribute(String name, Number value) {
        return addAttribute(name, value.toString());
    }

    /**
     * Adds an attribute with boolean value to given HTML element.
     *
     * @param name attribute name.
     * @param value attribute value.
     * @return this HTMLElement.
     */
    public final HTMLElement addAttribute(String name, boolean value) {
        return addAttribute(name, String.valueOf(value));
    }

    /**
     * Returns HTML code representing this HTMLElement object which can be
     * included into a web page.
     *
     * @return a string with the HTML code.
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("<");
        sb.append(name);

        if (!isEmpty(attributes)) {
            Set<String> attributeNames = attributes.keySet();
            for (String attributeName : attributeNames) {
                sb.append(" ");
                sb.append(attributeName);
                String attributeValue = attributes.get(attributeName);
                if (attributeValue != null) {
                    sb.append("=\"");
                    sb.append(attributeValue);
                    sb.append("\"");
                }
            }
        }

        if (!isEmpty(children)) {
            sb.append(">");

            Iterator iterator = children.iterator();
            while (iterator.hasNext()) {
                Object child = iterator.next();
                if (child != null) {
                    sb.append(child.toString());
                }
                //sb.append(" ");
            }

            sb.append("</");
            sb.append(name);
            sb.append(">");
        } else {
            sb.append("/>");
        }

        return sb.toString();
    }

    @Override
    public HTMLElement clone() {
        HTMLElement e = new HTMLElement(name);
        if (attributes != null) {
            e.attributes = (Map<String, String>) ((HashMap<String, String>) attributes).clone();
        }
        if (children != null) {
            e.children = (List) ((LinkedList) children).clone();
        }
        return e;
    }

    /**
     * Checks if given map is empty.
     *
     * @param map a map to check.
     * @return
     * <code>true</code> if map contains at least one element;
     * <code>false</code> if map is
     * <code>null</code> or contains no elements.
     */
    protected boolean isEmpty(Map map) {
        if (map == null) {
            return true;
        } else {
            return map.isEmpty();
        }
    }

    /**
     * Checks if given collection is empty.
     *
     * @param map a collection to check.
     * @return
     * <code>true</code> if collection contains at least element;
     * <code>false</code> if collection is
     * <code>null</code> or contains no elements.
     */
    protected boolean isEmpty(Collection collection) {
        if (collection == null) {
            return true;
        } else {
            return collection.isEmpty();
        }
    }
}
